#include "CH58x_common.h"
#include "zqm_global.h"
#include "zqm_module.h"
#include "hal_mcu.h"
#include "hal_uart.h"
#include "task_gather.h"
#include "task_iot.h"
#include "task_ota.h"

#include "hal_disp_key.h"
// ------------------------------------------------------------------------------------------------
#define IOT_UART_PKG_SIZE       (512+16)	    // 最大情况出现在OTA, 一帧大小为5+6+512
#define IOT_UART_RX_BUF_SIZE    (IOT_UART_PKG_SIZE * 2)
static ZQ_ALIGNED(4) uint8_t iot_uart_rx_buf[IOT_UART_RX_BUF_SIZE]; // 4 bytes align, reduce OTA copy. 582 write flash data must 4 bytes align

static void Cat1HeartbeatClean(void)
{
    if(zy_st.cat1_heart > 0) // 如果小于等于0，说明已经被关闭, 进入等待彻底下电的delay时间, 要等Cat1PowerOn初始化恢复
        zy_st.cat1_heart = 5 * 60 * 1000; // 5 minute, 断网上电会有很长的拨号尝试等不应期
}
void Cat1PowerOn(uint8_t reason)
{
    if (!GPIO_ReadPortPin(zy_ios.cat1) && (ZQ_COMM_PLATFORM == 8850 || ZQ_COMM_PLATFORM == 8910)) {
        M0DelayMs(50);
        GPIO_SetBits(zy_ios.cat1);
        m0_diag.cat1_start_cause = reason;
        zy_st.cat1_heart = 5 * 60 * 1000; // 5 minute, 断网上电会有很长的拨号尝试等不应期
        UartInit(IOT_UART_PORT, 230400, iot_uart_rx_buf, IOT_UART_RX_BUF_SIZE, NULL);      // 配置上位机通讯串口, 两个CPU之间有电平转换电路，不建议超过230400
    }
}
void Cat1PowerOff(void){
    if (ZQ_COMM_PLATFORM == 8850 || ZQ_COMM_PLATFORM == 8910) {
        GPIO_ResetBits(zy_ios.cat1);
    }
}
// monitor cat1 heartbeat, reset and reconnection
// must be called every TASK_PERIOD in mainloop
void Cat1HeartbeatMonitor(void)
{
    // cat1 heartbeat countdown (s)
    zy_st.cat1_heart -= TASK_PERIOD;
    if(zy_st.cat1_heart < -500) // after cat1 is powered off, wait some time then restart.
        Cat1PowerOn(START_CAUSE_C_HEARTBEAT);
    if (zy_st.cat1_heart <= 0) { // heartbeat overtime, reset cat1 after 500ms
        Cat1PowerOff();
    }
}
// buf是总发送buf(含包头位置); sz 是整个包(含包头)长度
int IotResponse(uint8_t cmd, uint8_t *buf, uint16_t len)
{
    ZqCommPkg *pkg = (ZqCommPkg *)buf;
    pkg->flag = COMM_PKG_HEAD_FLAG;
    pkg->len = len;
    pkg->cmd = cmd;
    pkg->checksum = 0;
    pkg->checksum = ZqGetChecksum(buf, len);
    UartSend(IOT_UART_PORT, buf, len);
    return len;
}

// 命令1 - 交换基础信息，下发cat1版本， 查询M0版本, ble mac等信息
static int IotResponseCmd01(ZqCommPkg* pkg)
{
    DtuC01RequestPkg *req = (DtuC01RequestPkg *)pkg->data;
    strcpy(cat1_sw_ver, req->cat1_sw_ver);    // 记录cat1 version
	uint8_t buf[sizeof(ZqCommPkg) + sizeof(DtuC01ResponsePkg)] = {0};

    DtuC01ResponsePkg *pResp = (DtuC01ResponsePkg *)(buf + sizeof(ZqCommPkg));
    pResp->com_protocol_ver = COMM_PROTOCOL_VER;
    pResp->bootmode = 3;      // 正常工作模式
    pResp->ctrl_state = zy_st.ctrl_state;
    pResp->display_panel_type = ZQ_DISPLAY_PANEL_TYPE;
    strcpy(pResp->m0_sw_ver, ZQ_SW_VER);
    // memcpy(pResp->ble_uuid, m0_diag.ble_uuid, sizeof(m0_diag.ble_uuid));
#if(ZQ_BLE_PLATFORM == 8910)
    memset(pResp->ble_mac, 0, sizeof(m0_diag.ble_mac));
#else
    memcpy(pResp->ble_mac, m0_diag.ble_mac, sizeof(m0_diag.ble_mac));
#endif
	strcpy(pResp->zq_ver_prefix, ZQ_VER_PREFIX);
	strcpy(pResp->akm_ctrl_type, AKM_CTRL_TYPE);
	strcpy(pResp->akm_dev_type, AKM_DEV_TYPE);
	strcpy(pResp->akm_module_type, AKM_MODULE_TYPE);
	pResp->akm_ctrl_func = AKM_CTRL_FUNC;

    pResp->m0_start_cause = m0_diag.m0_start_cause;
    pResp->m0_run_time = m0_diag.m0_run_time;
    pResp->cat1_start_cause = m0_diag.cat1_start_cause;
    IotResponse(0x01, buf, sizeof(buf));
    return 0;
}

// cmd02 心跳包(1s), 交换周期数据
int IotResponseCmd02(ZqCommPkg* pkg)
{
    Cat1HeartbeatClean();  // 重置cat1心跳包计数
    // do receive data
    if (sizeof(DtuC02RequestPkg) != (pkg->len - sizeof(ZqCommPkg))) {
        ZQLOG("Receive cmd02 data length err: %d", pkg->len);
        return -1;
    }
    DtuC02RequestPkg *pRecv = (DtuC02RequestPkg *)pkg->data;
    if(pRecv->_4g < 3) // 保护,限制一下
        zy_st._4g = pRecv->_4g;
    zy_st.wifi = (pRecv->wifi) ? 1 : 0;
    zy_st._4g_strength = pRecv->_4g_strength;
    //PmSetValue(pm_idx.f2, zy_st._4g_strength);
    PmForceSetValue(pm_idx.f2, zy_st._4g_strength, zy_st._4g_strength, zy_st._4g_strength);
#if (ZQ_BLE_PLATFORM == 8910)	    // 有蓝牙, 且蓝牙在8910平台
        zy_st.ble = pRecv->ble;
#endif // (ZQ_BLE_PLATFORM == 8910)
    // calibrate system time
    if(pRecv->cali_time > 0) 
        ZqmCaliSysTime(pRecv->cali_time);
        
    //if (zq_timer_s % 10 == 0) { // TODO 测试代码
    //    uint16_t year, mon, day, hour, min, sec;
    //    RTC_GetTime(&year, &mon, &day, &hour, &min, &sec);
    //    ZQLOG("Get system time: %04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec);
    //}

    // response
	uint8_t buf[sizeof(ZqCommPkg) + sizeof(DtuC02ResponsePkg)] = {0};
    DtuC02ResponsePkg* resp = (DtuC02ResponsePkg*)(buf + sizeof(ZqCommPkg));
    resp->d_temp = zy_st.d_temp; 				// 显示温度
    resp->m_temp = zy_st.m_temp; 				// 测量温度
    resp->load_vol = zy_st.load_vol;			// 负载电压
    resp->load_cur = zy_st.load_cur;			// 负载电流
    resp->load_pow = zy_st.load_pow;			// 负载功率
    resp->vbat = zy_st.vbat;					// 电池电压 mv, 0 - 4200, =NUM_INVALID_WORD 表示无电池
    resp->alarm = zy_st.alarm;					// 报警
    resp->ctrl_mode = zy_st.ctrl_mode;			// 控制器工作模式: ZqmCtrlMode
    resp->ctrl_state = zy_st.ctrl_state;		// 控制器工作状态: 0-关闭, 1-开启, 2-低功耗前期, 3-低功耗, 4-24H上报; ZyCtrlState
    resp->ctrl_switch = zy_st.ctrl_switch;		// 开关状态, on/off - 1/0; b0: remote_switch, b1: hard_switch, b2: soft_switch, 所以ctrl_switch=0x07表示开关全开
    resp->comp_mode = zy_st.comp_mode;			// 压机运行模式, 参见ZqCompModeEnum, 可能多个mode组合，但是当前实际按优先级运行
    resp->comp_state = zy_st.comp_state;		// 压机预计启停状态, 参见ZqCompStateEnum
    resp->fan_state = zy_st.fan_state;			// 风机工作状态, 参见ZqFanStateEnum
    resp->heater_state = zy_st.heater_state;	// 加热器启停状态, 参见ZqHeaterStateEnum
    resp->light_state = zy_st.light_state;		// 灯
    resp->door1_timer = zy_st.door1_timer;      // 门开计时s, <= 0: 门关, 由数据采样模块负责
    resp->door1_count = zy_st.door1_count;      // 门开计次, 由数据采样模块负责    
    resp->humman = zy_st.humman;                // 门开计次, 由数据采样模块负责人流量计数
    // resp->TS = TcGetTS();	                // 设定温度; r1~r2; ℃
    resp->TS = pms_spec[pm_idx.ts].val;	        // 设定温度; r1~r2; ℃ - 客人要求暂时不处理ECO相关的温度设置变化
    resp->r1 = pm_ctrl.r1;  	                // 设定温度最小值, n~TS ℃
    resp->r2 = pm_ctrl.r2;  	                // 设定温度最大值, TS~n ℃
    resp->AH = pm_ctrl.ah;  	                // 高温报警值, ℃
    resp->AL = pm_ctrl.al;  	                // 低温报警值, ℃
    resp->C1 = pm_ctrl.c1;  				    // 温度上回差, .1℃
    resp->C2 = pm_ctrl.c2;  				    // 温度下回差, .1℃
    resp->C3 = pm_ctrl.c3;  				    // 传感器故障开机时间, M
    resp->C4 = pm_ctrl.c4;  				    // 传感器故障停机时间, M
    resp->C5 = pm_ctrl.c5;  				    // 显示温度补偿值, .1℃
	resp->C7 = pm_ctrl.c7;  				    //最小停机时间, M
    resp->d1 = pm_ctrl.d1;  				    // 自然化霜/压机保护周期, H
    resp->d2 = pm_ctrl.d2;  				    // 自然化霜/压机保护时间, M
    // if (pRecv->clear) // 清除周期采样数据
    //     zy_st.door1_count = zy_st.humman = 0;
    IotResponse(0x02, buf, sizeof(buf));
    
    return 0;
}

// cmd03 远程设定参数
static void IotResponseCmd03_Response(DtuC03ReqRespPkg* pRecv)
{
    // response
    uint8_t buf[sizeof(ZqCommPkg) + sizeof(DtuC03ReqRespPkg)] = { 0 };
    DtuC03ReqRespPkg* pset = (DtuC03ReqRespPkg*)(buf + sizeof(ZqCommPkg));

    // if (pRecv->ctrl_mode == 0x80) must response all parameters
    pset->ctrl_mode = (pRecv->ctrl_mode == 0x80) ? zy_st.ctrl_mode : NUM_INVALID_BYTE; // 冷热柜特有的模式切换, cat1会下发两次cmd03，间隔100ms，第一次ctrl_mode=0/1，通知冷热切换，第二次ctrl_mode=0x80通知上报切换后的所有变更参数
    pset->ctrl_remote_switch = ((pRecv->ctrl_remote_switch == 0 || pRecv->ctrl_remote_switch == 1) || pRecv->ctrl_mode == 0x80) ? (zy_st.ctrl_switch & CTRL_REMOTE_SWITCH == CTRL_REMOTE_SWITCH) : NUM_INVALID_BYTE;
    pset->light_state = ((pRecv->light_state == 0 || pRecv->light_state == 1) || pRecv->ctrl_mode == 0x80) ? zy_st.light_state : NUM_INVALID_BYTE;
    pset->pm_TS = (IsWordValid(pRecv->pm_TS) || pRecv->ctrl_mode == 0x80) ? pm_ctrl.ts : NUM_INVALID_WORD;
    pset->pm_r1 = (IsWordValid(pRecv->pm_r1) || pRecv->ctrl_mode == 0x80) ? pm_ctrl.r1 : NUM_INVALID_WORD;
    pset->pm_r2 = (IsWordValid(pRecv->pm_r2) || pRecv->ctrl_mode == 0x80) ? pm_ctrl.r2 : NUM_INVALID_WORD;
    pset->pm_C5 = (IsWordValid(pRecv->pm_C5) || pRecv->ctrl_mode == 0x80) ? pm_ctrl.c5 : NUM_INVALID_WORD;
    pset->pm_AH = (IsWordValid(pRecv->pm_AH) || pRecv->ctrl_mode == 0x80) ? pm_ctrl.ah : NUM_INVALID_WORD;
    pset->pm_AL = (IsWordValid(pRecv->pm_AL) || pRecv->ctrl_mode == 0x80) ? pm_ctrl.al : NUM_INVALID_WORD;

    IotResponse(0x03, buf, sizeof(buf));
}
int IotResponseCmd03(ZqCommPkg *pkg)
{
    // do receive data
    if (sizeof(DtuC03ReqRespPkg) != (pkg->len - sizeof(ZqCommPkg))) {
        ZQLOG("Receive cmd03 data length error: %d", pkg->len);
        return -1;
    }
    DtuC03ReqRespPkg *pRecv = (DtuC03ReqRespPkg*)pkg->data;

    // 冷热柜特有的模式切换, cat1会下发两次cmd03，间隔100ms，第一次ctrl_mode=0/1，通知冷热切换，第二次ctrl_mode=0x80通知上报切换后的所有变更参数
#if (ZQ_BOX_MODE == (CTRL_MODE_COOL | CTRL_MODE_HOT)) // 冷热型
    if ((pRecv->ctrl_mode == 0 || pRecv->ctrl_mode == 1) && pRecv->ctrl_mode != zy_st.ctrl_mode) {   // switch mode TODO
        zy_st.ctrl_state = CTRL_STATE_MODE_SWITCH; // 设置模式切换，并且在下一个周期软重启
        IotResponseCmd03_Response(pRecv);
        return 0;
    }
    else if (pRecv->ctrl_mode == 0x80) {
        IotResponseCmd03_Response(pRecv);
        return 0;
    }
#endif // 冷热型
    // 远程开关机
    if (IsByteValid(pRecv->ctrl_remote_switch)){
        zy_st.ctrl_switch = pRecv->ctrl_remote_switch ? (zy_st.ctrl_switch | CTRL_REMOTE_SWITCH) : (zy_st.ctrl_switch & ~CTRL_REMOTE_SWITCH);
#if(CFG_DEVICE_TYPE == 760) // 760要记住远程开关机
        PmSave();
#endif
    }
    // 远程灯控
    if(ZQ_FUNC_LIGHT == 1 && (pRecv->light_state == 0 || pRecv->light_state == 1)) {
        if(zy_st.light_state != pRecv->light_state) 
            TcLightSoftSwitch();
    }
    // 报警取消条件 - 2 若远程调节过设定温度或高/低温报警值，则立即复位高/低温报警标志并按新设定值重新判断；TODO - 不考虑ECO模式影响
    if (IsWordValid(pRecv->pm_TS) || IsWordValid(pRecv->pm_AH) || IsWordValid(pRecv->pm_AL)) {
        if (pm_ctrl.ts != pRecv->pm_TS || pm_ctrl.ah != pRecv->pm_AH || pm_ctrl.al != pRecv->pm_AL)
            TgTemAlarmCancel(0);
    }
    bool changed = 0;
    changed |= PmSetValue(pm_idx.ts, pRecv->pm_TS);
    changed |= PmSetValue(pm_idx.r1, pRecv->pm_r1);
    changed |= PmSetValue(pm_idx.r2, pRecv->pm_r2);
    changed |= PmSetValue(pm_idx.c5, pRecv->pm_C5);
    changed |= PmSetValue(pm_idx.ah, pRecv->pm_AH);
    changed |= PmSetValue(pm_idx.al, pRecv->pm_AL);

    if(changed)
        PmSave();

    // response
    IotResponseCmd03_Response(pRecv);
    return 0;
}

// OTA flash erase cmd , discard
static int IotResponseCmd10(ZqCommPkg* pkg)
{
    OtaBegin(0);
    // response
    uint8_t buf[sizeof(ZqCommPkg) + 1] = { 0 };
    uint8_t* pPkg = buf + sizeof(ZqCommPkg);
    *pPkg = 1; // ACK
    IotResponse(0x10, buf, sizeof(buf));
    return 0;
}
// OTA flash write cmd,
static int IotResponseCmd11(ZqCommPkg* pkg)
{
    // write to flash
    OtaRecvData(pkg->data + 8, pkg->len - sizeof(ZqCommPkg) - 8);
    // response
    uint8_t buf[sizeof(ZqCommPkg) + 1] = { 0 };
    uint8_t* pPkg = buf + sizeof(ZqCommPkg);
    *pPkg = 1; // ACK
    IotResponse(0x11, buf, sizeof(buf));
    return 0;
}
// OTA flash data transform over and check checksum
static int IotResponseCmd12(ZqCommPkg* pkg)
{
    uint8_t ret = OtaCheckData(pkg->data[0]);
    // response
    uint8_t buf[sizeof(ZqCommPkg) + 1] = { 0 };
    uint8_t* pPkg = buf + sizeof(ZqCommPkg);
    *pPkg = ret; // ACK
    IotResponse(0x12, buf, sizeof(buf));
    if (ret) 
        OtaEndAndRestart();
    return 0;
}

void IotRecvRx(ZqCommPkg  *pkg)
{
    switch(pkg->cmd) 
    {
        case COMM_CMD_BASE:
            IotResponseCmd01(pkg);
            ZQLOG("IotResponseCmd01");
            break;
        case COMM_CMD_EXCH:
            IotResponseCmd02(pkg);
            ZQLOG("IotResponseCmd02");
            break;
        case COMM_CMD_SETUP:
            IotResponseCmd03(pkg);
            ZQLOG("IotResponseCmd03");
            break;
        case COMM_CMD_OTA_START:
            ZQLOG("IotResponseCmd10");
        	// spec 6.3.10 设备升级
            IotResponseCmd10(pkg);
            break;
        case COMM_CMD_OTA_DATA:
        	// spec 6.3.10 设备升级
            IotResponseCmd11(pkg);
            break;
        case COMM_CMD_OTA_END:
        	// spec 6.3.10 设备升级
            IotResponseCmd12(pkg);
            break;
        case COMM_CMD_SLEEP:
            IsCat1ReadyOff = 1;
            break;

        default:
            break;
    }

}
void TiTask(void)
{
    UartRecv(IOT_UART_PORT, IotRecvRx);
}

